import { cloneDeep } from 'lodash';
import { OnGlobalStateChangeCallback, MicroAppStateActions } from './interfaces';
let globalState = {};
const deps = {};

function emitGlobal(state, prevState) {
  Object.keys(deps).forEach(id => {
    if (deps[id] instanceof Function) {
      deps[id](cloneDeep(state), cloneDeep(prevState));
    }
  });
}

export function initGlobalState(state = {}) {
  if (state === globalState) {
    console.warn('[qiankun] state has not changed！');
  } else {
    const prevGlobalState = cloneDeep(globalState);
    globalState = cloneDeep(state);
    emitGlobal(globalState, prevGlobalState);
  }

  return getMicroAppStateActions(`global-${+new Date()}`, true);
}
export function getMicroAppStateActions(id, isMaster) {
  return {
    onGlobalStateChange(callback, fireImmediately) {
      if (!(callback instanceof Function)) {
        console.error('[qiankun] callback must be function!');
        return;
      }

      if (deps[id]) {
        console.warn(`[qiankun] '${id}' global listener already exists before this, new listener will overwrite it.`);
      }

      deps[id] = callback;
      const cloneState = cloneDeep(globalState);

      if (fireImmediately) {
        callback(cloneState, cloneState);
      }
    },

    setGlobalState(state = {}) {
      if (state === globalState) {
        console.warn('[qiankun] state has not changed！');
        return false;
      }

      const changeKeys = [];
      const prevGlobalState = cloneDeep(globalState);
      globalState = cloneDeep(Object.keys(state).reduce((_globalState, changeKey) => {
        if (isMaster || _globalState.hasOwnProperty(changeKey)) {
          changeKeys.push(changeKey);
          return Object.assign(_globalState, {
            [changeKey]: state[changeKey]
          });
        }

        console.warn(`[qiankun] '${changeKey}' not declared when init state！`);
        return _globalState;
      }, globalState));

      if (changeKeys.length === 0) {
        console.warn('[qiankun] state has not changed！');
        return false;
      }

      emitGlobal(globalState, prevGlobalState);
      return true;
    },

    offGlobalStateChange() {
      delete deps[id];
      return true;
    }

  };
}